package is.api.demo.isgateway.utils;

import is.api.demo.isgateway.support.CipherConstants;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.provider.JCEECPrivateKey;
import org.bouncycastle.jce.provider.JCEECPublicKey;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.encoders.Hex;

import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;

/**
 * sm2加密解密验签工具类
 *
 * @author chen.chen
 */
public abstract class Sm2Utils {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }


    /**
     * 获取签名
     *
     * @param privateKey 私钥byte数组
     * @param sourceData 待签名原文byte数组
     * @return 已签名byte数组
     */
    public static byte[] sign(byte[] privateKey, byte[] sourceData)
            throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        //解析公钥
        ECPrivateKey ecPrivateKey = Sm2KeyUtils.parseSM2PrivateKey(privateKey);
        //签名
        return sign(ecPrivateKey, sourceData);
    }

    /**
     * 获取签名
     *
     * @param privateKey 私钥,16进制字符串64字节
     * @param sourceData 待签名原文
     * @return 已签名byte数组
     */
    public static byte[] sign(String privateKey, String sourceData)
            throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        //签名
        byte[] privateKeyBytes = Hex.decode(privateKey);
        return sign(privateKeyBytes, IsGateWayStringUtils.getBytesUtf8(sourceData));
    }

    /**
     * 获取签名,16进制字符串
     *
     * @param privateKey 私钥
     * @param sourceData 待签名原文
     * @return 已签名byte数组
     */
    public static byte[] sign(PrivateKey privateKey, byte[] sourceData)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        // 生成SM2sign with sm3 签名验签算法实例
        Signature signature = Signature.getInstance(CipherConstants.DIGEST_ALGORITHM, new BouncyCastleProvider());
        // 签名需要使用私钥，使用私钥 初始化签名实例
        signature.initSign(privateKey);
        // 写入签名原文到算法中
        signature.update(sourceData);
        // 计算签名值
        return signature.sign();
    }

    /**
     * 签名验证
     *
     * @param publicKey  公钥
     * @param sourceData 签名原文
     * @param signData   签名结果byte数组
     * @return 验证签名结果, true为验签成功, false为验签失败
     */
    public static boolean verifySign(byte[] publicKey, byte[] sourceData, byte[] signData)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        //解析公钥
        ECPublicKey ecPublicKey = Sm2KeyUtils.parseSM2PublicKey(publicKey);
        //验证
        return verifySign(ecPublicKey, sourceData, signData);
    }

    /**
     * 签名验证
     *
     * @param publicKey  公钥  04 + 16进制字符串130字节
     * @param sourceData 签名原文
     * @param signData   签名结果16进制字符串
     * @return 验证签名结果, true为验签成功, false为验签失败
     */
    public static boolean verifySign(String publicKey, String sourceData, String signData)
            throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        //验证
        byte[] publicKeyBytes = Hex.decode(publicKey);
        byte[] signByte = Hex.decode(signData);
        return verifySign(publicKeyBytes,
                IsGateWayStringUtils.getBytesUtf8(sourceData),
                signByte);
    }

    /**
     * 签名验证
     *
     * @param publicKey  公钥
     * @param sourceData 签名原文
     * @param signData   签名结果byte数组
     * @return 验证签名结果, true为验签成功, false为验签失败
     */
    public static boolean verifySign(PublicKey publicKey, byte[] sourceData, byte[] signData)
            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        //获取算法提供者
        Provider provider = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME);
        //签名对象获取
        Signature signature = Signature.getInstance(CipherConstants.DIGEST_ALGORITHM, provider);
        // 验签需要使用公钥，使用公钥 初始化签名实例
        signature.initVerify(publicKey);
        // 写入待验签的签名原文到算法中
        signature.update(sourceData);
        //验证
        return signature.verify(signData);
    }

    /**
     * SM2加密算法
     *
     * @param publicKey 公钥
     * @param data      明文数据
     * @return 加密结果字符串, 16进制
     */
    public static String encrypt(PublicKey publicKey, String data)
            throws InvalidCipherTextException {

        ECPublicKeyParameters ecPublicKeyParameters = null;
        //获取椭圆曲线规格,由ecc参数决定
        if (publicKey instanceof BCECPublicKey) {
            BCECPublicKey bcecPublicKey = (BCECPublicKey) publicKey;
            ECParameterSpec ecParameterSpec = bcecPublicKey.getParameters();
            ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
                    ecParameterSpec.getG(), ecParameterSpec.getN());
            ecPublicKeyParameters = new ECPublicKeyParameters(bcecPublicKey.getQ(), ecDomainParameters);
        } else if (publicKey instanceof JCEECPublicKey) {
            JCEECPublicKey jceecPublicKey = (JCEECPublicKey) publicKey;
            ECParameterSpec parameters = jceecPublicKey.getParameters();
            ECDomainParameters ecDomainParameters = new ECDomainParameters(parameters.getCurve(),
                    parameters.getG(), parameters.getN());
            ecPublicKeyParameters = new ECPublicKeyParameters(jceecPublicKey.getQ(), ecDomainParameters);
        }

        SM2Engine sm2Engine = new SM2Engine();

        sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));

        byte[] arrayOfBytes = null;
        byte[] in = IsGateWayStringUtils.getBytesUtf8(data);
        arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
        return Hex.toHexString(arrayOfBytes);
    }

    /**
     * SM2加密算法
     *
     * @param publicKey 公钥 04 + 16进制字符串130字节
     * @param data      明文数据
     * @return 加密结果字符串, 16进制
     */
    public static String encrypt(String publicKey, String data)
            throws InvalidCipherTextException {
        //加密
        return encrypt(Sm2KeyUtils.parseSM2PublicKey(Hex.decode(publicKey)), data);
    }

    /**
     * SM2解密算法
     *
     * @param privateKey 私钥
     * @param cipherData 密文数据
     * @return 解密结果
     */
    public static String decrypt(PrivateKey privateKey, String cipherData)
            throws InvalidCipherTextException {
        byte[] cipherDataByte = Hex.decode(cipherData);

        ECPrivateKeyParameters ecPrivateKeyParameters = null;
        //获取椭圆曲线规格,由ecc参数决定
        if (privateKey instanceof BCECPrivateKey) {
            BCECPrivateKey bcecPrivateKey = (BCECPrivateKey) privateKey;
            ECParameterSpec ecParameterSpec = bcecPrivateKey.getParameters();
            ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
                    ecParameterSpec.getG(), ecParameterSpec.getN());

            ecPrivateKeyParameters = new ECPrivateKeyParameters(bcecPrivateKey.getD(),
                    ecDomainParameters);
        } else if (privateKey instanceof JCEECPrivateKey) {
            JCEECPrivateKey jceecPrivateKey = (JCEECPrivateKey) privateKey;
            ECParameterSpec parameters = jceecPrivateKey.getParameters();
            ECDomainParameters ecDomainParameters = new ECDomainParameters(parameters.getCurve(),
                    parameters.getG(), parameters.getN());
            ecPrivateKeyParameters = new ECPrivateKeyParameters(jceecPrivateKey.getD(), ecDomainParameters);
        }

        SM2Engine sm2Engine = new SM2Engine();
        sm2Engine.init(false, ecPrivateKeyParameters);

        byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
        return new String(arrayOfBytes, StandardCharsets.UTF_8);
    }

    /**
     * SM2解密算法
     *
     * @param privateKey 私钥,16进制字符串,64字节
     * @param cipherData 密文16进制数据
     * @return 解密结果
     */
    public static String decrypt(String privateKey, String cipherData)
            throws InvalidCipherTextException {
        return decrypt(Sm2KeyUtils.parseSM2PrivateKey(Hex.decode(privateKey)), cipherData);
    }
}
